/*
 * Copyright 2014 JootMir Project
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 * Support: http://www.jootmir.org
 */
package org.jootmir.client.core;

import java.awt.Point;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;

import org.jootmir.client.core.exception.JMir2Exception;
import org.lwjgl.LWJGLException;
import org.lwjgl.input.Keyboard;
import org.lwjgl.input.Mouse;

/**
 * 事件处理
 * <br>
 * 逐帧自动处理事件并封装(当前帧是处理前一帧的信息，前第二帧的信息也会被保存以供不时之需)。
 * <br>
 * 针对鼠标事件，会自动整合鼠标在上一帧中移动的位置并给出 <b>最终位置</b> ；对鼠标按键的按下和弹起会(按鼠标按键分组)计算其按下弹起的次数并给出其 <b>最终的状态</b> (按下或弹起)；对于鼠标滚轮的滚动会 <b>累加</b> 上下方向的值并给出
 * <br>
 * 针对键盘事件，会自动整合键盘按键在上一帧中按下弹起的情况(分组计算)并给出最终的按键 <b>状态</b> 和按键的按下弹起次数总和
 * <br>
 * 下文中上一帧则指前第二帧，当前帧则指上一帧
 * 
 * @author ShawRyan
 */
public final class Event {
	
	/**
	 * 枚举 - 鼠标按键
	 */
	public enum MouseButton {
		/** 鼠标左键 */
		Left,
		/** 鼠标右键 */
		Right,
		/** 鼠标中键 */
		Middle,
		/** 未按下鼠标按键 */
		None;
		
		/**
		 * 从LWJGL的鼠标按键索引还原鼠标按键
		 * 
		 * @param index
		 * 		LWJGL中鼠标按键索引
		 * @return
		 * 		鼠标按键枚举值
		 */
		public static MouseButton valueOf(int index) {
			switch(index) {
			case 0: return Left;
			case 1: return Right;
			case 2: return Middle;
			default: return None;
			}
		}
	}
	/**
	 * 枚举 - 键盘按键
	 */
	public enum Key {
		/** 未按下键 */
		KEY_NONE(Keyboard.KEY_NONE),
		KEY_ESCAPE(Keyboard.KEY_ESCAPE),
		KEY_1(Keyboard.KEY_1),
		KEY_2(Keyboard.KEY_2),
		KEY_3(Keyboard.KEY_3),
		KEY_4(Keyboard.KEY_4),
		KEY_5(Keyboard.KEY_5),
		KEY_6(Keyboard.KEY_6),
		KEY_7(Keyboard.KEY_7),
		KEY_8(Keyboard.KEY_8),
		KEY_9(Keyboard.KEY_9),
		KEY_0(Keyboard.KEY_0),
		KEY_MINUS(Keyboard.KEY_MINUS),
		KEY_EQUALS(Keyboard.KEY_EQUALS),
		KEY_BACK(Keyboard.KEY_BACK),
		KEY_TAB(Keyboard.KEY_TAB),
		KEY_Q(Keyboard.KEY_Q),
		KEY_W(Keyboard.KEY_W),
		KEY_E(Keyboard.KEY_E),
		KEY_R(Keyboard.KEY_R),
		KEY_T(Keyboard.KEY_T),
		KEY_Y(Keyboard.KEY_Y),
		KEY_U(Keyboard.KEY_U),
		KEY_I(Keyboard.KEY_I),
		KEY_O(Keyboard.KEY_O),
		KEY_P(Keyboard.KEY_P),
		KEY_LBRACKET(Keyboard.KEY_LBRACKET),
		KEY_RBRACKET(Keyboard.KEY_RBRACKET),
		KEY_RETURN(Keyboard.KEY_RETURN),
		KEY_LCONTROL(Keyboard.KEY_LCONTROL),
		KEY_A(Keyboard.KEY_A),
		KEY_S(Keyboard.KEY_S),
		KEY_D(Keyboard.KEY_D),
		KEY_F(Keyboard.KEY_F),
		KEY_G(Keyboard.KEY_G),
		KEY_H(Keyboard.KEY_H),
		KEY_J(Keyboard.KEY_J),
		KEY_K(Keyboard.KEY_K),
		KEY_L(Keyboard.KEY_L),
		KEY_SEMICOLON(Keyboard.KEY_SEMICOLON),
		KEY_APOSTROPHE(Keyboard.KEY_APOSTROPHE),
		KEY_GRAVE(Keyboard.KEY_GRAVE),
		KEY_LSHIFT(Keyboard.KEY_LSHIFT),
		KEY_BACKSLASH(Keyboard.KEY_BACKSLASH),
		KEY_Z(Keyboard.KEY_Z),
		KEY_X(Keyboard.KEY_X),
		KEY_C(Keyboard.KEY_C),
		KEY_V(Keyboard.KEY_V),
		KEY_B(Keyboard.KEY_B),
		KEY_N(Keyboard.KEY_N),
		KEY_M(Keyboard.KEY_M),
		KEY_COMMA(Keyboard.KEY_COMMA),
		KEY_PERIOD(Keyboard.KEY_PERIOD),
		KEY_SLASH(Keyboard.KEY_SLASH),
		KEY_RSHIFT(Keyboard.KEY_RSHIFT),
		KEY_MULTIPLY(Keyboard.KEY_MULTIPLY),
		KEY_LMENU(Keyboard.KEY_LMENU),
		KEY_SPACE(Keyboard.KEY_SPACE),
		KEY_CAPITAL(Keyboard.KEY_CAPITAL),
		KEY_F1(Keyboard.KEY_F1),
		KEY_F2(Keyboard.KEY_F2),
		KEY_F3(Keyboard.KEY_F3),
		KEY_F4(Keyboard.KEY_F4),
		KEY_F5(Keyboard.KEY_F5),
		KEY_F6(Keyboard.KEY_F6),
		KEY_F7(Keyboard.KEY_F7),
		KEY_F8(Keyboard.KEY_F8),
		KEY_F9(Keyboard.KEY_F9),
		KEY_F10(Keyboard.KEY_F10),
		KEY_NUMLOCK(Keyboard.KEY_NUMLOCK),
		KEY_SCROLL(Keyboard.KEY_SCROLL),
		KEY_NUMPAD7(Keyboard.KEY_NUMPAD7),
		KEY_NUMPAD8(Keyboard.KEY_NUMPAD8),
		KEY_NUMPAD9(Keyboard.KEY_NUMPAD9),
		KEY_SUBTRACT(Keyboard.KEY_SUBTRACT),
		KEY_NUMPAD4(Keyboard.KEY_NUMPAD4),
		KEY_NUMPAD5(Keyboard.KEY_NUMPAD5),
		KEY_NUMPAD6(Keyboard.KEY_NUMPAD6),
		KEY_ADD(Keyboard.KEY_ADD),
		KEY_NUMPAD1(Keyboard.KEY_NUMPAD1),
		KEY_NUMPAD2(Keyboard.KEY_NUMPAD2),
		KEY_NUMPAD3(Keyboard.KEY_NUMPAD3),
		KEY_NUMPAD0(Keyboard.KEY_NUMPAD0),
		KEY_DECIMAL(Keyboard.KEY_DECIMAL),
		KEY_F11(Keyboard.KEY_F11),
		KEY_F12(Keyboard.KEY_F12),
		KEY_F13(Keyboard.KEY_F13),
		KEY_F14(Keyboard.KEY_F14),
		KEY_F15(Keyboard.KEY_F15),
		KEY_F16(Keyboard.KEY_F16),
		KEY_F17(Keyboard.KEY_F17),
		KEY_F18(Keyboard.KEY_F18),
		KEY_KANA(Keyboard.KEY_KANA),
		KEY_F19(Keyboard.KEY_F19),
		KEY_CONVERT(Keyboard.KEY_CONVERT),
		KEY_NOCONVERT(Keyboard.KEY_NOCONVERT),
		KEY_YEN(Keyboard.KEY_YEN),
		KEY_NUMPADEQUALS(Keyboard.KEY_NUMPADEQUALS),
		KEY_CIRCUMFLEX(Keyboard.KEY_CIRCUMFLEX),
		KEY_AT(Keyboard.KEY_AT),
		KEY_COLON(Keyboard.KEY_COLON),
		KEY_UNDERLINE(Keyboard.KEY_UNDERLINE),
		KEY_KANJI(Keyboard.KEY_KANJI),
		KEY_STOP(Keyboard.KEY_STOP),
		KEY_AX(Keyboard.KEY_AX),
		KEY_UNLABELED(Keyboard.KEY_UNLABELED),
		KEY_NUMPADENTER(Keyboard.KEY_NUMPADENTER),
		KEY_RCONTROL(Keyboard.KEY_RCONTROL),
		KEY_SECTION(Keyboard.KEY_SECTION),
		KEY_NUMPADCOMMA(Keyboard.KEY_NUMPADCOMMA),
		KEY_DIVIDE(Keyboard.KEY_DIVIDE),
		KEY_SYSRQ(Keyboard.KEY_SYSRQ),
		KEY_RMENU(Keyboard.KEY_RMENU),
		KEY_FUNCTION(Keyboard.KEY_FUNCTION),
		KEY_PAUSE(Keyboard.KEY_PAUSE),
		KEY_HOME(Keyboard.KEY_HOME),
		KEY_UP(Keyboard.KEY_UP),
		KEY_PRIOR(Keyboard.KEY_PRIOR),
		KEY_LEFT(Keyboard.KEY_LEFT),
		KEY_RIGHT(Keyboard.KEY_RIGHT),
		KEY_END(Keyboard.KEY_END),
		KEY_DOWN(Keyboard.KEY_DOWN),
		KEY_NEXT(Keyboard.KEY_NEXT),
		KEY_INSERT(Keyboard.KEY_INSERT),
		KEY_DELETE(Keyboard.KEY_DELETE),
		KEY_CLEAR(Keyboard.KEY_CLEAR),
		KEY_LMETA(Keyboard.KEY_LMETA),
		KEY_RMETA(Keyboard.KEY_RMETA),
		KEY_APPS(Keyboard.KEY_APPS),
		KEY_POWER(Keyboard.KEY_POWER),
		KEY_SLEEP(Keyboard.KEY_SLEEP);
		
		private int code;
		Key(int code) {
			this.code = code;
		}
		/** 获取键盘编码<br>此编码为{@link Keyboard}中的虚拟按键码 */
		public int code() {
			return code;
		}
		/** 从lwjgl键盘索引转化键盘按键枚举值 */
		public static Key valueOf(int lwjgl_keyboard_key) {
			switch(lwjgl_keyboard_key) {
			case Keyboard.KEY_ESCAPE: return KEY_ESCAPE;
			case Keyboard.KEY_1: return KEY_1;
			case Keyboard.KEY_2: return KEY_2;
			case Keyboard.KEY_3: return KEY_3;
			case Keyboard.KEY_4: return KEY_4;
			case Keyboard.KEY_5: return KEY_5;
			case Keyboard.KEY_6: return KEY_6;
			case Keyboard.KEY_7: return KEY_7;
			case Keyboard.KEY_8: return KEY_8;
			case Keyboard.KEY_9: return KEY_9;
			case Keyboard.KEY_0: return KEY_0;
			case Keyboard.KEY_MINUS: return KEY_MINUS;
			case Keyboard.KEY_EQUALS: return KEY_EQUALS;
			case Keyboard.KEY_BACK: return KEY_BACK;
			case Keyboard.KEY_TAB: return KEY_TAB;
			case Keyboard.KEY_Q: return KEY_Q;
			case Keyboard.KEY_W: return KEY_W;
			case Keyboard.KEY_E: return KEY_E;
			case Keyboard.KEY_R: return KEY_R;
			case Keyboard.KEY_T: return KEY_T;
			case Keyboard.KEY_Y: return KEY_Y;
			case Keyboard.KEY_U: return KEY_U;
			case Keyboard.KEY_I: return KEY_I;
			case Keyboard.KEY_O: return KEY_O;
			case Keyboard.KEY_P: return KEY_P;
			case Keyboard.KEY_LBRACKET: return KEY_LBRACKET;
			case Keyboard.KEY_RBRACKET: return KEY_RBRACKET;
			case Keyboard.KEY_RETURN: return KEY_RETURN;
			case Keyboard.KEY_LCONTROL: return KEY_LCONTROL;
			case Keyboard.KEY_A: return KEY_A;
			case Keyboard.KEY_S: return KEY_S;
			case Keyboard.KEY_D: return KEY_D;
			case Keyboard.KEY_F: return KEY_F;
			case Keyboard.KEY_G: return KEY_G;
			case Keyboard.KEY_H: return KEY_H;
			case Keyboard.KEY_J: return KEY_J;
			case Keyboard.KEY_K: return KEY_K;
			case Keyboard.KEY_L: return KEY_L;
			case Keyboard.KEY_SEMICOLON: return KEY_SEMICOLON;
			case Keyboard.KEY_APOSTROPHE: return KEY_APOSTROPHE;
			case Keyboard.KEY_GRAVE: return KEY_GRAVE;
			case Keyboard.KEY_LSHIFT: return KEY_LSHIFT;
			case Keyboard.KEY_BACKSLASH: return KEY_BACKSLASH;
			case Keyboard.KEY_Z: return KEY_Z;
			case Keyboard.KEY_X: return KEY_X;
			case Keyboard.KEY_C: return KEY_C;
			case Keyboard.KEY_V: return KEY_V;
			case Keyboard.KEY_B: return KEY_B;
			case Keyboard.KEY_N: return KEY_N;
			case Keyboard.KEY_M: return KEY_M;
			case Keyboard.KEY_COMMA: return KEY_COMMA;
			case Keyboard.KEY_PERIOD: return KEY_PERIOD;
			case Keyboard.KEY_SLASH: return KEY_SLASH;
			case Keyboard.KEY_RSHIFT: return KEY_RSHIFT;
			case Keyboard.KEY_MULTIPLY: return KEY_MULTIPLY;
			case Keyboard.KEY_LMENU: return KEY_LMENU;
			case Keyboard.KEY_SPACE: return KEY_SPACE;
			case Keyboard.KEY_CAPITAL: return KEY_CAPITAL;
			case Keyboard.KEY_F1: return KEY_F1;
			case Keyboard.KEY_F2: return KEY_F2;
			case Keyboard.KEY_F3: return KEY_F3;
			case Keyboard.KEY_F4: return KEY_F4;
			case Keyboard.KEY_F5: return KEY_F5;
			case Keyboard.KEY_F6: return KEY_F6;
			case Keyboard.KEY_F7: return KEY_F7;
			case Keyboard.KEY_F8: return KEY_F8;
			case Keyboard.KEY_F9: return KEY_F9;
			case Keyboard.KEY_F10: return KEY_F10;
			case Keyboard.KEY_NUMLOCK: return KEY_NUMLOCK;
			case Keyboard.KEY_SCROLL: return KEY_SCROLL;
			case Keyboard.KEY_NUMPAD7: return KEY_NUMPAD7;
			case Keyboard.KEY_NUMPAD8: return KEY_NUMPAD8;
			case Keyboard.KEY_NUMPAD9: return KEY_NUMPAD9;
			case Keyboard.KEY_SUBTRACT: return KEY_SUBTRACT;
			case Keyboard.KEY_NUMPAD4: return KEY_NUMPAD4;
			case Keyboard.KEY_NUMPAD5: return KEY_NUMPAD5;
			case Keyboard.KEY_NUMPAD6: return KEY_NUMPAD6;
			case Keyboard.KEY_ADD: return KEY_ADD;
			case Keyboard.KEY_NUMPAD1: return KEY_NUMPAD1;
			case Keyboard.KEY_NUMPAD2: return KEY_NUMPAD2;
			case Keyboard.KEY_NUMPAD3: return KEY_NUMPAD3;
			case Keyboard.KEY_NUMPAD0: return KEY_NUMPAD0;
			case Keyboard.KEY_DECIMAL: return KEY_DECIMAL;
			case Keyboard.KEY_F11: return KEY_F11;
			case Keyboard.KEY_F12: return KEY_F12;
			case Keyboard.KEY_F13: return KEY_F13;
			case Keyboard.KEY_F14: return KEY_F14;
			case Keyboard.KEY_F15: return KEY_F15;
			case Keyboard.KEY_F16: return KEY_F16;
			case Keyboard.KEY_F17: return KEY_F17;
			case Keyboard.KEY_F18: return KEY_F18;
			case Keyboard.KEY_KANA: return KEY_KANA;
			case Keyboard.KEY_F19: return KEY_F19;
			case Keyboard.KEY_CONVERT: return KEY_CONVERT;
			case Keyboard.KEY_NOCONVERT: return KEY_NOCONVERT;
			case Keyboard.KEY_YEN: return KEY_YEN;
			case Keyboard.KEY_NUMPADEQUALS: return KEY_NUMPADEQUALS;
			case Keyboard.KEY_CIRCUMFLEX: return KEY_CIRCUMFLEX;
			case Keyboard.KEY_AT: return KEY_AT;
			case Keyboard.KEY_COLON: return KEY_COLON;
			case Keyboard.KEY_UNDERLINE: return KEY_UNDERLINE;
			case Keyboard.KEY_KANJI: return KEY_KANJI;
			case Keyboard.KEY_STOP: return KEY_STOP;
			case Keyboard.KEY_AX: return KEY_AX;
			case Keyboard.KEY_UNLABELED: return KEY_UNLABELED;
			case Keyboard.KEY_NUMPADENTER: return KEY_NUMPADENTER;
			case Keyboard.KEY_RCONTROL: return KEY_RCONTROL;
			case Keyboard.KEY_SECTION: return KEY_SECTION;
			case Keyboard.KEY_NUMPADCOMMA: return KEY_NUMPADCOMMA;
			case Keyboard.KEY_DIVIDE: return KEY_DIVIDE;
			case Keyboard.KEY_SYSRQ: return KEY_SYSRQ;
			case Keyboard.KEY_RMENU: return KEY_RMENU;
			case Keyboard.KEY_FUNCTION: return KEY_FUNCTION;
			case Keyboard.KEY_PAUSE: return KEY_PAUSE;
			case Keyboard.KEY_HOME: return KEY_HOME;
			case Keyboard.KEY_UP: return KEY_UP;
			case Keyboard.KEY_PRIOR: return KEY_PRIOR;
			case Keyboard.KEY_LEFT: return KEY_LEFT;
			case Keyboard.KEY_RIGHT: return KEY_RIGHT;
			case Keyboard.KEY_END: return KEY_END;
			case Keyboard.KEY_DOWN: return KEY_DOWN;
			case Keyboard.KEY_NEXT: return KEY_NEXT;
			case Keyboard.KEY_INSERT: return KEY_INSERT;
			case Keyboard.KEY_DELETE: return KEY_DELETE;
			case Keyboard.KEY_CLEAR: return KEY_CLEAR;
			case Keyboard.KEY_LMETA: return KEY_LMETA;
			case Keyboard.KEY_RMETA: return KEY_RMETA;
			case Keyboard.KEY_APPS: return KEY_APPS;
			case Keyboard.KEY_POWER: return KEY_POWER;
			case Keyboard.KEY_SLEEP: return KEY_SLEEP;
			default: return KEY_NONE;
			}
		}
	}
	/** 初始化事件处理 */
	static void initialize() {
		try {
			Keyboard.create();
			Mouse.create();
		} catch (LWJGLException e) {
			throw JMir2Exception.valueOf(e, JMir2Exception.EXCEPTION_TYPE_ENV);
		}
	}
	/** 游戏主线程自动逐帧调用 */
	static void poll() {
		// 先把帧信息前置
		lastMouseX = mouseX;
		lastMouseY = mouseY;
		lastMouseWheelUp = mouseWheelUp;
		lastMouseWheelDown = mouseWheelDown;
		lastMouseButtonModifiers = mouseButtonModifiers;
		lastMouseButtonStates = mouseButtonStates;
		lastMouseDownCount = mouseDownCount;
		lastMouseUpCount = mouseUpCount;
		lastMouseWheelUpCount = mouseWheelUpCount;
		lastMouseWheelDownCount = mouseWheelDownCount;
		lastKeyModifiers = keyModifiers;
		lastKeyStates = keyStates;
		lastKeyDownCount = keyDownCount;
		lastKeyUpCount = keyUpCount;
		lastCharReceived = charReceived;
		lastCharReceivedTime = charReceivedTime;
		// 鼠标位置
		Mouse.poll();
		mouseX = Mouse.getX();
		mouseY = Graphics.get().height - Mouse.getY();
		while(Mouse.next()) {
			// 鼠标事件
			// 鼠标按键
			int index = Mouse.getEventButton();
			if(index == -1) {
				// 滚动？
				int dwheel = Mouse.getDWheel();
				if(dwheel != 0) {
					if(dwheel > 0) {
						mouseWheelUp += dwheel;
						++mouseWheelUpCount;
					} else {
						mouseWheelDown += dwheel;
						++mouseWheelDownCount;
					}
				}/* else {// 移动，移动只会造成鼠标位置变化，此变化会最终被整合体现，Mouse.poll()之后会更新鼠标位置}*/
			} else {
				// 鼠标按下或弹起
				MouseButton button = MouseButton.valueOf(index);
				if(button != MouseButton.None) {
					if(!mouseButtonModifiers.contains(button)) {
						if(mouseButtonStates.length == mouseButtonModifiers.size()) {
							boolean[] states0 = new boolean[mouseButtonStates.length + 5]; // 增量为5
							System.arraycopy(mouseButtonStates, 0, states0, 0, mouseButtonStates.length);
							mouseButtonStates = states0;
						}
						mouseButtonModifiers.add(button);
						mouseDownCount.put(button, 0);
						mouseUpCount.put(button, 0);
					}
					if(Mouse.getEventButtonState()) {
						// 按下
						mouseDownCount.put(button, mouseDownCount.get(button) + 1);
					} else {
						// 抬起
						// TODO 丢失拖动状态？？
						mouseUpCount.put(button, mouseUpCount.get(button) + 1);
					}
				}
			}
		}
		for(int i = 0; i < mouseButtonModifiers.size(); ++i) {
			mouseButtonStates[i] = Mouse.isButtonDown(mouseButtonModifiers.get(i).ordinal());
		}
		// 判断拖动
		endDraging.clear(); // 与拖动的按键集合不同，停止拖动的动作具有暂时性，每帧都可能出现。“过了这个村，就没这个店了”
		for(MouseButton button : MouseButton.values()) {
			if(draging.contains(button)) {
				if(!isMouseButtonPress(button)) { // 拖动的按键被弹起了
					draging.remove(button);
					endDraging.add(button);
					//dragingStartPosition.remove(button);
					// 事实上，鼠标按键数量不多，不用在意，我们还需要得到已经停止拖动的按键的起始位置
				}
			} else if(isMouseButtonPressLast(button) && isMouseButtonPress(button) && isMouseMoved()) {
				draging.add(button);
				dragingStartPosition.put(button, getMousePositionLast());
			}
		}
		// 键盘
		int receivedCharCount = 0;
		Keyboard.poll();
		while(Keyboard.next()) {
			Key key = Key.valueOf(Keyboard.getEventKey());
			if(key != Key.KEY_NONE) {
				if(!keyModifiers.contains(key)) {
					keyModifiers.add(key);
					keyDownCount.put(key, 0);
					keyUpCount.put(key, 0);
					keyStates = new boolean[keyStates.length + 1];
				}
				if(Keyboard.getEventKeyState()) {
					// 按下
					keyDownCount.put(key, keyDownCount.get(key) + 1);
				} else {
					// 抬起
					keyUpCount.put(key, keyUpCount.get(key) + 1);
				}
			}
			char c = Keyboard.getEventCharacter();
			if(c == ' ' || c == '　' || c == '\t' || c == '\r' || (!Character.isWhitespace(c) && !Character.isISOControl(c))) {
				receivedCharCount++;
				if(charReceived.length == receivedCharCount - 1) { // 增量扩充数组
					char[] chars0 = new char[charReceived.length + 5];
					System.arraycopy(charReceived, 0, chars0, 0, charReceived.length);
					charReceived = chars0;
					long[] times0 = new long[charReceivedTime.length + 5];
					System.arraycopy(charReceivedTime, 0, times0, 0, charReceivedTime.length);
					charReceivedTime = times0;
				}
				charReceived[receivedCharCount - 1] = c;
				charReceivedTime[receivedCharCount - 1] = Keyboard.getEventNanoseconds();
			}
		}
		for(int i = 0; i < keyModifiers.size(); ++i) {
			keyStates[i] = Keyboard.isKeyDown(keyModifiers.get(i).code());
		}
		// 修剪字符数组
		if(receivedCharCount == 0) {
			charReceived = new char[0];
			charReceivedTime = new long[0];
		} else if(charReceived.length != receivedCharCount) {
			char[] chars0 = new char[charReceived.length + 5];
			System.arraycopy(charReceived, 0, chars0, 0, receivedCharCount);
			charReceived = chars0;
			long[] times0 = new long[charReceivedTime.length + 5];
			System.arraycopy(charReceivedTime, 0, times0, 0, receivedCharCount);
			charReceivedTime = times0;
		}
	}

	private static int lastMouseX = 0;
	private static int lastMouseY = 0;
	private static int lastMouseWheelUp = 0;
	private static int lastMouseWheelDown = 0;
	private static List<MouseButton> lastMouseButtonModifiers = new ArrayList<MouseButton>();
	private static boolean[] lastMouseButtonStates = new boolean[10];
	private static Map<MouseButton, Integer> lastMouseDownCount = new Hashtable<MouseButton, Integer>();
	private static Map<MouseButton, Integer> lastMouseUpCount = new Hashtable<MouseButton, Integer>();
	private static int lastMouseWheelUpCount = 0;
	private static int lastMouseWheelDownCount = 0;
	private static List<Key> lastKeyModifiers = new ArrayList<Key>();
	private static boolean[] lastKeyStates = new boolean[10];
	private static Map<Key, Integer> lastKeyDownCount = new Hashtable<Key, Integer>();
	private static Map<Key, Integer> lastKeyUpCount = new Hashtable<Key, Integer>();
	private static char[] lastCharReceived = new char[10];
	private static long[] lastCharReceivedTime = new long[10];
	
	/**
	 * 获取上一帧结束时鼠标位置
	 * 
	 * @return 鼠标位置
	 */
	public static Point getMousePositionLast() {
		return new Point(lastMouseX, lastMouseY);
	}
	/**
	 * 获取上一帧被抬起或按下过的鼠标集合
	 * <br>
	 * 集合不重复，以(第一次产生事件的)先后顺序排序
	 * 
	 * @return 被按下或抬起过的鼠标按键
	 */
	public static List<MouseButton> getEventButtonsLast() {
		return lastMouseButtonModifiers;
	}
	/**
	 * 获取上一帧特定鼠标按键是否被按下或抬起过
	 * 
	 * @param button
	 * 		鼠标按键
	 * @return 该按键是否被按下或抬起过
	 */
	public static boolean isMouseButtonChangedLast(MouseButton button) {
		return lastMouseButtonModifiers.contains(button);
	}
	/**
	 * 获取上一帧结束时某个鼠标按键是否处于被按下的状态
	 * 
	 * @param button
	 * 		鼠标按键
	 * @return 该按键是否被按下着
	 */
	public static boolean isMouseButtonPressLast(MouseButton button) {
		int indexOf = lastMouseButtonModifiers.indexOf(button);
		if(indexOf < 0 || indexOf >= lastMouseButtonStates.length) return false;
		return lastMouseButtonStates[indexOf];
	}
	/**
	 * 获取上一帧中某鼠标按键总共被按下次数
	 * 
	 * @param button
	 * 		鼠标按键
	 * @return 该按键被按下的次数
	 */
	public static int getMouseButtonDownCountLast(MouseButton button) {
		Integer count = lastMouseDownCount.get(button);
		if(count == null) return 0;
		return count;
	}
	/**
	 * 获取上一帧中某鼠标按键总共被抬起次数
	 * 
	 * @param button
	 * 		鼠标按键
	 * @return 该按键被抬起的次数
	 */
	public static int getMouseButtonUpCountLast(MouseButton button) {
		Integer count = lastMouseUpCount.get(button);
		if(count == 0) return 0;
		return count;
	}
	/**
	 * 获取上一帧中鼠标滚轮是否发生过滚动
	 * 
	 * @return 鼠标是否进行上下滚动
	 */
	public static boolean isMouseWheelLast() {
		return lastMouseWheelUp > 0 || lastMouseWheelDown > 0;
	}
	/**
	 * 获取上一帧中鼠标滚轮是否向上滚动过
	 * 
	 * @return 鼠标是否向上滚动
	 */
	public static boolean isMouseWheelUpLast() {
		return lastMouseWheelUp > 0;
	}
	/**
	 * 获取上一帧中鼠标是否想下滚动过
	 * 
	 * @return 鼠标是否向下滚动
	 */
	public static boolean isMouseWheelDownLast() {
		return lastMouseWheelDown > 0;
	}
	/**
	 * 获取上一帧中鼠标总过滚动的距离(上下相加)
	 * 
	 * @return 鼠标滚动总距离
	 */
	public static int getMouseWheelLast() {
		return lastMouseWheelUp - lastMouseWheelDown;
	}
	/**
	 * 获取上一帧中鼠标滚动距离绝对值
	 * 
	 * @return 鼠标滚动距离绝对值
	 * @see #getMouseWheelDirectLast()
	 */
	public static int getMouseWheelAbsLast() {
		return Math.abs(lastMouseWheelUp - -lastMouseWheelDown);
	}
	/**
	 * 获取上一帧中鼠标滚动方向(绝对值)
	 * 
	 * @return 0:未滚动 1:向上 -1:向下
	 * @see #getMouseWheelAbsLast()
	 */
	public static int getMouseWheelDirectLast() {
		if(lastMouseWheelUp == 0 && lastMouseWheelDown == 0) return 0;
		if(lastMouseWheelUp > lastMouseWheelDown) return 1;
		return -1;
	}
	/**
	 * 获取上一帧中鼠标向上滚动总次数
	 * 
	 * @return 鼠标向上滚动次数
	 */
	public static int getMouseWHeelUpCountLast() {
		return lastMouseWheelUpCount;
	}
	/**
	 * 获取上一帧中鼠标向下滚动总次数
	 * 
	 * @return 鼠标向下滚动次数
	 */
	public static int getMouseWHeelDownCountLast() {
		return lastMouseWheelDownCount;
	}
	/**
	 * 获取上一帧中按下或抬起过的所有键盘按键
	 * <br>
	 * 不重复，以事件发生先后顺序排序
	 * 
	 * @return 按下或抬起过的所有键盘按键
	 */
	public static List<Key> getEventKeysLast() {
		return lastKeyModifiers;
	}
	/**
	 * 获取上一帧中某特定键盘按键是否被按下或抬起过
	 * 
	 * @param key
	 * 		键盘按键
	 * @return 该按键是否被按下或抬起过
	 */
	public static boolean isKeyChangedLast(Key key) {
		return lastKeyModifiers.contains(key);
	}
	/**
	 * 上一帧结束时特定键盘按键是否处于按下状态
	 * 
	 * @param key
	 * 		键盘按键
	 * @return 该按键是否处于按下状态
	 */
	public static boolean isKeyPressLast(Key key) {
		int indexOf = lastKeyModifiers.indexOf(key);
		if(indexOf < 0 || indexOf >= lastKeyStates.length) return false;
		return lastKeyStates[indexOf];
	}
	/**
	 * 获取上一帧中某特定键盘按键总共被按下次数
	 * 
	 * @param key
	 * 		键盘按键
	 * @return 按下总次数
	 */
	public static int getKeyDownCountLast(Key key) {
		Integer count = lastKeyDownCount.get(key);
		if(count == null) return 0;
		return count;
	}
	/**
	 * 获取上一帧中某特定键盘按键总共被抬起次数
	 * 
	 * @param key
	 * 		键盘按键
	 * @return 抬起总次数
	 */
	public static int getKeyUpCountLast(Key key) {
		Integer count = lastKeyUpCount.get(key);
		if(count == null) return 0;
		return count;
	}
	/**
	 * 获取上一帧中程序收到的字符
	 * <br>
	 * 字符由操作系统底层发送消息，lwjgl事件线程接收并整理
	 * <br>
	 * 输入法的输入或普通键盘按键可能会产生字符
	 * 
	 * @return 程序收到的字符
	 * @see #getReceivedCharTimeLast()
	 */
	public static char[] getReceivedCharLast() {
		return lastCharReceived;
	}
	/**
	 * 获取上一帧中收到各个字符的时间
	 * <br>
	 * 时间只能做参考，如时间的先后(这个在字符数组中已经体现)和是否通过输入法输入的短语(一个短语中每个字符的接收时间都相同)
	 * 
	 * @return 程序收到字符的时间
	 * @see #getReceivedCharLast()
	 */
	public static long[] getReceivedCharTimeLast() {
		return lastCharReceivedTime;
	}
	
	private static int mouseX = 0;
	private static int mouseY = 0;
	private static int mouseWheelUp = 0;
	private static int mouseWheelDown = 0;
	private static List<MouseButton> mouseButtonModifiers = new ArrayList<MouseButton>();
	private static boolean[] mouseButtonStates = new boolean[10];
	private static Map<MouseButton, Integer> mouseDownCount = new Hashtable<MouseButton, Integer>();
	private static Map<MouseButton, Integer> mouseUpCount = new Hashtable<MouseButton, Integer>();
	private static int mouseWheelUpCount = 0;
	private static int mouseWheelDownCount = 0;
	private static List<Key> keyModifiers = new ArrayList<Key>();
	private static boolean[] keyStates = new boolean[10];
	private static Map<Key, Integer> keyDownCount = new Hashtable<Key, Integer>();
	private static Map<Key, Integer> keyUpCount = new Hashtable<Key, Integer>();
	private static char[] charReceived = new char[10];
	private static long[] charReceivedTime = new long[10];
	
	private static List<MouseButton> draging = new ArrayList<MouseButton>();
	private static List<MouseButton> endDraging = new ArrayList<MouseButton>();
	private static Map<MouseButton, Point> dragingStartPosition = new Hashtable<MouseButton, Point>();
	
	/**
	 * 获取当前帧结束时鼠标位置
	 * 
	 * @return 鼠标位置
	 */
	public static Point getMousePosition() {
		return new Point(mouseX, mouseY);
	}
	/**
	 * 获取当前帧被抬起或按下过的鼠标集合
	 * <br>
	 * 集合不重复，以(第一次产生事件的)先后顺序排序
	 * 
	 * @return 被按下或抬起过的鼠标按键
	 */
	public static List<MouseButton> getEventButtons() {
		return mouseButtonModifiers;
	}
	/**
	 * 获取当前帧特定鼠标按键是否被按下或抬起过
	 * 
	 * @param button
	 * 		鼠标按键
	 * @return 该按键是否被按下或抬起过
	 */
	public static boolean isMouseButtonChanged(MouseButton button) {
		return mouseButtonModifiers.contains(button);
	}
	/**
	 * 获取当前帧结束时某个鼠标按键是否处于被按下的状态
	 * 
	 * @param button
	 * 		鼠标按键
	 * @return 该按键是否被按下着
	 */
	public static boolean isMouseButtonPress(MouseButton button) {
		int indexOf = mouseButtonModifiers.indexOf(button);
		if(indexOf < 0 || indexOf >= mouseButtonStates.length) return false;
		return mouseButtonStates[indexOf];
	}
	/**
	 * 获取当前帧中某鼠标按键总共被按下次数
	 * 
	 * @param button
	 * 		鼠标按键
	 * @return 该按键被按下的次数
	 */
	public static int getMouseButtonDownCount(MouseButton button) {
		Integer count = mouseDownCount.get(button);
		if(count == null) return 0;
		return count;
	}
	/**
	 * 获取当前帧中某鼠标按键总共被抬起次数
	 * 
	 * @param button
	 * 		鼠标按键
	 * @return 该按键被抬起的次数
	 */
	public static int getMouseButtonUpCount(MouseButton button) {
		Integer count = mouseUpCount.get(button);
		if(count == 0) return 0;
		return count;
	}
	/**
	 * 获取当前帧中鼠标滚轮是否发生过滚动
	 * 
	 * @return 鼠标是否进行上下滚动
	 */
	public static boolean isMouseWheel() {
		return mouseWheelUp > 0 || mouseWheelDown > 0;
	}
	/**
	 * 获取当前帧中鼠标滚轮是否向上滚动过
	 * 
	 * @return 鼠标是否向上滚动
	 */
	public static boolean isMouseWheelUp() {
		return mouseWheelUp > 0;
	}
	/**
	 * 获取当前帧中鼠标是否想下滚动过
	 * 
	 * @return 鼠标是否向下滚动
	 */
	public static boolean isMouseWheelDown() {
		return mouseWheelDown > 0;
	}
	/**
	 * 获取当前帧中鼠标总过滚动的距离(上下相加)
	 * 
	 * @return 鼠标滚动总距离
	 */
	public static int getMouseWheel() {
		return mouseWheelUp - mouseWheelDown;
	}
	/**
	 * 获取当前帧中鼠标滚动距离绝对值
	 * 
	 * @return 鼠标滚动距离绝对值
	 * @see #getMouseWheelDirect()
	 */
	public static int getMouseWheelAbs() {
		return Math.abs(mouseWheelUp - -mouseWheelDown);
	}
	/**
	 * 获取当前帧中鼠标滚动方向(绝对值)
	 * 
	 * @return 0:未滚动 1:向上 -1:向下
	 * @see #getMouseWheelAbs()
	 */
	public static int getMouseWheelDirect() {
		if(mouseWheelUp == 0 && mouseWheelDown == 0) return 0;
		if(mouseWheelUp > mouseWheelDown) return 1;
		return -1;
	}
	/**
	 * 获取当前帧中鼠标向上滚动总次数
	 * 
	 * @return 鼠标向上滚动次数
	 */
	public static int getMouseWHeelUpCount() {
		return mouseWheelUpCount;
	}
	/**
	 * 获取当前帧中鼠标向下滚动总次数
	 * 
	 * @return 鼠标向下滚动次数
	 */
	public static int getMouseWHeelDownCount() {
		return mouseWheelDownCount;
	}
	/**
	 * 获取当前帧中按下或抬起过的所有键盘按键
	 * <br>
	 * 不重复，以事件发生先后顺序排序
	 * 
	 * @return 按下或抬起过的所有键盘按键
	 */
	public static List<Key> getEventKeys() {
		return keyModifiers;
	}
	/**
	 * 获取当前帧中某特定键盘按键是否被按下或抬起过
	 * 
	 * @param key
	 * 		键盘按键
	 * @return 该按键是否被按下或抬起过
	 */
	public static boolean isKeyChanged(Key key) {
		return keyModifiers.contains(key);
	}
	/**
	 * 当前帧结束时特定键盘按键是否处于按下状态
	 * 
	 * @param key
	 * 		键盘按键
	 * @return 该按键是否处于按下状态
	 */
	public static boolean isKeyPress(Key key) {
		int indexOf = keyModifiers.indexOf(key);
		if(indexOf < 0 || indexOf >= keyStates.length) return false;
		return keyStates[indexOf];
	}
	/**
	 * 获取当前帧中某特定键盘按键总共被按下次数
	 * 
	 * @param key
	 * 		键盘按键
	 * @return 按下总次数
	 */
	public static int getKeyDownCount(Key key) {
		Integer count = keyDownCount.get(key);
		if(count == null) return 0;
		return count;
	}
	/**
	 * 获取当前帧中某特定键盘按键总共被抬起次数
	 * 
	 * @param key
	 * 		键盘按键
	 * @return 抬起总次数
	 */
	public static int getKeyUpCount(Key key) {
		Integer count = keyUpCount.get(key);
		if(count == null) return 0;
		return count;
	}
	/**
	 * 获取当前帧中程序收到的字符
	 * <br>
	 * 字符由操作系统底层发送消息，lwjgl事件线程接收并整理
	 * <br>
	 * 输入法的输入或普通键盘按键可能会产生字符
	 * 
	 * @return 程序收到的字符
	 * @see #getReceivedCharTime()
	 */
	public static char[] getReceivedChar() {
		return charReceived;
	}
	/**
	 * 获取当前帧中收到各个字符的时间
	 * <br>
	 * 时间只能做参考，如时间的先后(这个在字符数组中已经体现)和是否通过输入法输入的短语(一个短语中每个字符的接收时间都相同)
	 * 
	 * @return 程序收到字符的时间
	 * @see #getReceivedChar()
	 */
	public static long[] getReceivedCharTime() {
		return charReceivedTime;
	}
	
	/**
	 * 两帧中鼠标是否移动过
	 * 
	 * @return 鼠标位置是否改变
	 */
	public static boolean isMouseMoved() {
		return mouseX != lastMouseX || mouseY != lastMouseY;
	}
	/**
	 * 是否正在拖动过程中
	 * <br>
	 * 如果上一帧(指代前面倒数第二帧)结束时鼠标键位处于按下状态并在当前帧(指代上一帧，因为此时处理的是前一帧信息)鼠标位置改动了，当然，当前帧鼠标按键也必须处于按下状态
	 * <br>
	 * 如果中途鼠标被快速按下抬起再按下(如果帧率高应该很少出现)则不会丢失拖动状态
	 * <br>
	 * XXX 此方法有延迟，如果在一帧中(用户操作很快或者帧数较低)时用户就完成了按下鼠标+移动鼠标的动作，也无法判断已经进行了拖动，需要在下一帧才知晓
	 * 
	 * @param button
	 * 		鼠标按键
	 * @return 鼠标按键是否正在进行拖动操作
	 */
	public static boolean isDraging(MouseButton button) {
		return draging.contains(button);
	}
	/**
	 * 是否停止拖动
	 * <br>
	 * 如果当前帧有正在拖动的按键抬起了，则它属于“停止拖动”
	 * <br>
	 * 停止动作在前一帧的不算
	 * 
	 * @param button
	 * 		鼠标按键
	 * @return 鼠标按键是否停止拖动
	 */
	public static boolean isEndDraging(MouseButton button) {
		return endDraging.contains(button);
	}
	/**
	 * 获取鼠标按键开始拖动的位置
	 * <br>
	 * 即使该鼠标已经停止拖动
	 * 
	 * @param button
	 * 		鼠标按键
	 * @return 拖动起始位置
	 */
	public static Point getDragingStartPosition(MouseButton button) {
		return dragingStartPosition.get(button);
	}
}